home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume25 / mrandom < prev    next >
Encoding:
Text File  |  1991-12-13  |  29.8 KB  |  880 lines

  1. Newsgroups: comp.sources.unix
  2. Path: pa.dec.com!vixie
  3. From: vixie@pa.dec.com (Paul Vixie)
  4. Subject: v25i023: A random number generator with persistent state
  5. Message-ID: <1991Dec14.033407.26343@PA.dec.com>
  6. Sender: news@PA.dec.com (News)
  7. Organization: DEC Palo Alto
  8. Date: Sat, 14 Dec 91 03:34:07 GMT
  9. Approved: vixie@pa.dec.com
  10. Lines: 868
  11.  
  12. Submitted-By: Clark Thomborson <cthombor@gw.d.umn.edu>
  13. Posting-Number: Volume 25, Issue 23
  14. Archive-Name: mrandom
  15.  
  16. I wrote this package to overcome some troubles I had with the random() 
  17. package.  I had been saving random()'s state table to a disk file, then
  18. restarting the random sequence in the next program run.  I discovered some
  19. seriously non-random behavior in the numbers resulting from this practice.
  20. Further investigation (including examination of the object code for
  21. random() -- painful!) showed me that it is necessary to count the number of
  22. calls to random() in order to safely restart it.  Hence the enclosed code.
  23.  
  24. [ I added all, clean, and install targets to the Makefile.  --vix ]
  25.  
  26. #! /bin/sh
  27. # This is a shell archive.  Remove anything before this line, then unpack
  28. # it by saving it into a file and typing "sh file".  To overwrite existing
  29. # files, type "sh file -c".  You can also feed this as standard input via
  30. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  31. # will see the following message at the end:
  32. #        "End of archive 1 (of 1)."
  33. # Contents:  MANIFEST Makefile README mrandom.3 mrandom.c mrandom.h
  34. #   mrtest.c
  35. # Wrapped by vixie@cognition.pa.dec.com on Fri Dec 13 14:28:31 1991
  36. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  37. if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  38.   echo shar: Will not clobber existing file \"'MANIFEST'\"
  39. else
  40. echo shar: Extracting \"'MANIFEST'\" \(331 characters\)
  41. sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
  42. X   File Name        Archive #    Description
  43. X-----------------------------------------------------------
  44. X MANIFEST                   1    This shipping list
  45. X Makefile                   1    
  46. X README                     1    
  47. X mrandom.3                  1    
  48. X mrandom.c                  1    
  49. X mrandom.h                  1    
  50. X mrtest.c                   1    
  51. END_OF_FILE
  52. if test 331 -ne `wc -c <'MANIFEST'`; then
  53.     echo shar: \"'MANIFEST'\" unpacked with wrong size!
  54. fi
  55. # end of 'MANIFEST'
  56. fi
  57. if test -f 'Makefile' -a "${1}" != "-c" ; then 
  58.   echo shar: Will not clobber existing file \"'Makefile'\"
  59. else
  60. echo shar: Extracting \"'Makefile'\" \(267 characters\)
  61. sed "s/^X//" >'Makefile' <<'END_OF_FILE'
  62. CFLAGS= -O
  63. X
  64. all: mrtest
  65. X
  66. clean:
  67. X    -rm *.o
  68. X    -rm mrtest
  69. X
  70. install:; @-echo "just link mrandom.o into your own programs as needed."
  71. X
  72. mrtest: mrandom.h mrandom.o mrtest.c
  73. X    cc $(CFLAGS) -o mrtest mrtest.c mrandom.o
  74. X
  75. mrandom.o: mrandom.h mrandom.c
  76. X    cc $(CFLAGS) -c mrandom.c
  77. END_OF_FILE
  78. if test 267 -ne `wc -c <'Makefile'`; then
  79.     echo shar: \"'Makefile'\" unpacked with wrong size!
  80. fi
  81. # end of 'Makefile'
  82. fi
  83. if test -f 'README' -a "${1}" != "-c" ; then 
  84.   echo shar: Will not clobber existing file \"'README'\"
  85. else
  86. echo shar: Extracting \"'README'\" \(1662 characters\)
  87. sed "s/^X//" >'README' <<'END_OF_FILE'
  88. I wrote this package to overcome some troubles I had with the random() 
  89. package.  I had been saving random()'s state table to a disk file, then
  90. restarting the random sequence in the next program run.  I discovered some
  91. seriously non-random behavior in the numbers resulting from this practice.
  92. XFurther investigation (including examination of the object code for
  93. random() -- painful!) showed me that it is necessary to count the number of
  94. calls to random() in order to safely restart it.  Hence the enclosed code.
  95. X
  96. I believe random(), with my modifications, is superior in many respects
  97. to its competition within 4.3bsd Unix, namely rand() and rand48().
  98. Those generators are based on a multiplicative congruential scheme,
  99. which makes them difficult to use in applications where one is generating
  100. points uniformly distributed on the unit square.  Certainly rand() has
  101. the problem that a high-resolution 2-d plot of the x-y points generated by
  102. X    for (i=0; i<n; i++) {
  103. X      x[i] = rand()/MAXLONG;
  104. X      y[i] = rand()/MAXLONG;
  105. X    } 
  106. will show stripes.  Using rand()%m is suicidal, since its low-order bits
  107. are known to be cyclic.  Perhaps I'm selling rand48() short, since I haven't
  108. examined it in detail: quite possibly its constants were chosen to minimize
  109. the separation between the stripes in a 2-d plot.
  110. X
  111. To help you get started, I have also enclosed a test-driver called mrtest.
  112. X
  113. Please let me know if you find any defects in the sequence generated
  114. by the default seed built into mrtest.  I picked that seed for
  115. sentimental reasons: it was the last number generated by my old,
  116. and now discredited, random() sequence.
  117. X
  118. X                        Clark Thomborson
  119. X                        cthombor@gw.d.umn.edu
  120. END_OF_FILE
  121. if test 1662 -ne `wc -c <'README'`; then
  122.     echo shar: \"'README'\" unpacked with wrong size!
  123. fi
  124. # end of 'README'
  125. fi
  126. if test -f 'mrandom.3' -a "${1}" != "-c" ; then 
  127.   echo shar: Will not clobber existing file \"'mrandom.3'\"
  128. else
  129. echo shar: Extracting \"'mrandom.3'\" \(7553 characters\)
  130. sed "s/^X//" >'mrandom.3' <<'END_OF_FILE'
  131. X.TH MRANDOM 3 "11 September 1991"
  132. X.SH NAME
  133. mrandom, frandom, init_rng, save_rng, restart_rng, reconstruct_rng, describe_rng \- wrappers for 4.3bsd random
  134. X.SH SYNOPSIS
  135. X.nf
  136. X.B long  mrandom(m)
  137. X.B long m;
  138. X.LP
  139. X.B double frandom()
  140. X.LP
  141. X.B int init_rng(seed, filename)
  142. X.B long seed;
  143. X.B char *filename;
  144. X.LP
  145. X.B int  save_rng(filename)
  146. X.B char *filename;
  147. X.LP 
  148. X.B int restart_rng(filename)
  149. X.B char *filename;
  150. X.LP
  151. X.B void reconstruct_rng(seed, count1, count2)
  152. X.B long seed;
  153. X.B long count1;
  154. X.B long count2;
  155. X.LP
  156. X.B char  *describe_rng(rngid)
  157. X.B char  rngid[RNGIDSTRLEN];
  158. X.fi
  159. X.IX  "mrandom function"  ""  "\fLmrandom\fP \(em generate random integer mod m"
  160. X.IX  "frandom function"  ""  "\fLfrandom\fP \(em generate random 64-bit float"
  161. X.IX  "init_rng function"  ""  "\fLinit_rng\fP \(em initialize random generator"
  162. X.IX  "save_rng function"  "" "\fLsave_rng\fP \(em save random generator to file"
  163. X.IX  "restart_rng function"  "" "\fLrestart_rng\fP \(em restart random generator from file"
  164. X.IX  "reconstruct_rng function"  ""  "\fLreconstruct_rng\fP \(em restore random generator from (seed, count1, count2) values"
  165. X.IX  "describe_rng function"  "" "\fLdescribe_rng\fP \(em construct short string describing generator state" 
  166. X.IX  "random number generator"  "\fLmrandom\fP"
  167. X.IX  "random number generator"  "\fLfrandom\fP"
  168. X.IX  "random number generator"  "\fLinit_rng\fP"
  169. X.IX  "random number generator"  "\fLsave_rng\fP"
  170. X.IX  "random number generator"  "\fLrestart_rng\fP"
  171. X.IX  "random number generator"  "\fLreconstruct_rng\fP"
  172. X.IX  "random number generator"  "\fLdescribe_rng\fP"
  173. X.IX  "generate random numbers"  "\fLmrandom\fP"
  174. X.IX  "generate random numbers"  "\fLfrandom\fP"
  175. X.IX  "generate random numbers"  "\fLinit_rng\fP"
  176. X.IX  "generate random numbers"  "\fLsave_rng\fP"
  177. X.IX  "generate random numbers"  "\fLrestart_rng\fP"
  178. X.IX  "generate random numbers"  "\fLreconstruct_rng\fP"
  179. X.IX  "generate random numbers"  "\fLdescribe_rng\fP"
  180. X.SH DESCRIPTION
  181. X.LP
  182. X.I mrandom(m)
  183. generates random integers in the range 0 to m-1, using a call to the
  184. X4.3bsd random() function.
  185. This is a non-linear additive feedback random number generator.
  186. The main advantage of mrandom(m) over random() is that you can save the
  187. complete state of the random number generator to a disk file,
  188. for later continuation of the same pseudorandom sequence.
  189. The period of mrandom(m)'s pseudorandom output is thus guaranteed to be long.
  190. In contrast, random()'s period can be quite short if you repeatedly
  191. save and restore its state using setstate(), because some of random()'s
  192. state is private.
  193. X.LP
  194. To use mrandom(), you must first initialize the random number generator.
  195. The easiest way to do this is to make a call to 
  196. X.I init_rng(seed, filename).
  197. If all goes well, this call will return the value 1,
  198. indicating that a file has been created
  199. describing the initial state of your generator.
  200. Hereafter, this file will be called an RNG state file.
  201. X.LP
  202. X.I init_rng
  203. will perform some error checks on the RNG state file after it is written,
  204. returning the value 0 if a problem is detected.
  205. X.LP
  206. After an
  207. X.I init_rng(seed, filename)
  208. call, the random number generator is in the same state as it would
  209. be if you had directly called
  210. X.I srandom(seed)
  211. or
  212. X.I initstate(seed, state, 128)
  213. on a ``fresh'' copy of the random() code.
  214. X.LP
  215. Warning: do not write your own calls to
  216. X.I srandom(), initstate(), random(),
  217. or
  218. X.I setstate()
  219. if you are using the mrandom package.
  220. If you do so, the long-period advantages of mrandom will be lost,
  221. and a later call to save_rng or restore_rng may fail.
  222. X.LP
  223. It is good experimental practice to preserve copies of
  224. your RNG state files, in case you ever want to reproduce a result.
  225. The state file is ASCII text, so it is portable across systems.
  226. X.LP
  227. Another good experimental practice is to ``stamp'' each printout
  228. with a record of the initial RNG state.
  229. You can do this by including a copy of the RNG state file, although
  230. this is a dozen lines long and a few hundred characters.
  231. A more convenient method is to use a call to
  232. X.I describe_rng(rngid).
  233. This puts a one-line short-form of the current RNG state in the
  234. character string rngid.
  235. X.LP
  236. X.I describe_rng(rngid)
  237. returns the value of rngid, for convenience.
  238. To print this short-form RNG descriptor, for example,
  239. you can execute printf(describe_rng(rngid)).
  240. Note: you must declare storage for rngid in your own program with
  241. a declaration such as
  242. X.I char rngid[RNGIDSTRLEN].
  243. X.I RNGIDSTRLEN
  244. is defined with the value 80 in the current release of mrandom.
  245. X.LP
  246. The short-form description provided by
  247. X.I describe_rng
  248. can be used, at a later date, to reconstruct the state of the RNG. 
  249. This can be a slow process, since it involves seeding a virgin copy
  250. of the random() generator, then calling random() a number of times.
  251. The relevant call in the mrandom package is
  252. X.I reconstruct_rng(seed, count1, count2),
  253. where seed is the initial seed, count1 is the total number of calls to 
  254. random() since initialization, mod one billion,
  255. and count2 is a ``div one billion'' counter.
  256. X.LP
  257. I hope you never have occasion to call
  258. X.I reconstruct_rng
  259. if count2 is greater than zero!
  260. This is one reason to preserve occasional copies of the complete
  261. RNG state, as recorded in the RNG state file.
  262. If you have a state file, then
  263. X.I restart_rng(filename)
  264. will always give rapid results.
  265. If the RNG state file is internally inconsistent,
  266. restart_rng will return 0, otherwise it returns 1.
  267. X.LP
  268. Just before exiting a program that uses mrandom, a call to
  269. X.I save_rng(filename)
  270. will preserve the final state of the RNG on disk.
  271. Note: I have found it helpful to make a
  272. save_rng call only upon normal exit from
  273. my experimental codes.  In case of an error exit, it is convenient
  274. to leave the RNG state file in its initial condition.
  275. This makes it easy to retry the experiment after fixing the bug.
  276. X.SH AUTHOR
  277. Clark Thomborson, cthombor@gw.d.umn.edu
  278. X.SH DIAGNOSTICS
  279. If the error-checking code in
  280. X.I init_rng, restart_rng,
  281. and
  282. X.I save_rng
  283. discovers a problem, an error message is printed on the stderr stream.
  284. Also, if
  285. X.I reconstruct_rng
  286. is called with count2 > 0, a warning message is printed on stderr.
  287. X.LP
  288. A demonstration routine, named
  289. X.I mrtest.c,
  290. is included with this software distribution to help get you started with the
  291. X.I mrandom
  292. package.
  293. X.SH "SEE ALSO"
  294. random(3), rand(3C), drand48(3)
  295. X.SH BUGS
  296. A little slower than
  297. X.I random().
  298. X.LP
  299. As far as I know,
  300. it is a subject of current research to decide if the pseudorandom sequence of
  301. X.I mrandom()
  302. is superior to that of a high-precision multiplicative generator such as
  303. X.I drand48().
  304. A low-precision multiplicative generator, such as
  305. X.I rand(3C),
  306. will cause problems in many applications, since non-random behavior
  307. is noticeable in the least-significant twenty bits of its output.
  308. X.LP
  309. Currently, the best theoretical guarantees for pseudorandomness come from the
  310. theory of universal hash functions.
  311. The idea is to use a high-precision multiplicative generator with
  312. random coefficients.
  313. Thus one might consider using the mrandom package to generate random
  314. coefficients, modulo large primes, for the drand48 pseudorandom number
  315. generator.
  316. X.LP
  317. By examination of the object code for random(), I deduce that
  318. it uses the following algorithm in its default (31-word table)
  319. configuration.
  320. X
  321. X.ID
  322. long random()
  323. X{
  324. X   static int j=4, k=1;
  325. X   long r;   
  326. X   rngstate[j] += rngstate[k];
  327. X   r = (rngstate[j]>>1) & 0x7fffffff;
  328. X   j++;
  329. X   if (j>31) j=1;
  330. X   k++;
  331. X   if (k>31) k=1;
  332. X   return(r);
  333. X
  334. X}
  335. X.DE
  336. X
  337. The initial values for rngstate are obtained from a linear congruential
  338. generator.  Any defects of this scheme will be inherited by mrandom.
  339. END_OF_FILE
  340. if test 7553 -ne `wc -c <'mrandom.3'`; then
  341.     echo shar: \"'mrandom.3'\" unpacked with wrong size!
  342. fi
  343. # end of 'mrandom.3'
  344. fi
  345. if test -f 'mrandom.c' -a "${1}" != "-c" ; then 
  346.   echo shar: Will not clobber existing file \"'mrandom.c'\"
  347. else
  348. echo shar: Extracting \"'mrandom.c'\" \(10569 characters\)
  349. sed "s/^X//" >'mrandom.c' <<'END_OF_FILE'
  350. X/*
  351. X *        mrandom.c
  352. X *
  353. X *    Wrapper for random(), allowing safe restart.
  354. X *
  355. X *    Original Implementation:
  356. X *        Clark Thomborson, September 1991
  357. X *
  358. X *    This material is based upon work supported by the National
  359. X *    Science Foundation under grant number MIP-9023238.  The
  360. X *    Government has certain rights in this material.
  361. X *
  362. X *    Any opinions, findings, and conclusions or recommendations
  363. X *    expressed in this material are those of the author and do
  364. X *    not necessarily reflect the view of the National Science
  365. X *    Foundation.
  366. X *
  367. X *    This code is neither copyrighted nor patented.
  368. X */
  369. X
  370. X#include <values.h> /* we need MAXLONG */
  371. X#include <stdio.h>  /* we need FILE */
  372. X#include "mrandom.h"
  373. X
  374. X#define RNGSTATELENGTH    32 /* legal values are 2, 8, 16, 32, 64 */
  375. X#define RNGSTATE0    3  /* rngstate[0] for table length 32 */
  376. X#define BILLION    1000000000 /* convenient modulus for a 32-bit counter */
  377. X
  378. X/* Data in our RNG statefile */
  379. long    RNGseed;    /* the seed originally used to initialize rngstate */
  380. long    RNGcount1;  /* mod-BILLION counter of random() calls */
  381. long    RNGcount2;  /* div-BILLION counter */
  382. long    rngstate[RNGSTATELENGTH]; /* an array passed to random() */
  383. long    RNGnextval; /* random()'s next output */
  384. X
  385. X/* Format of our RNG statefile */
  386. X#define RNGfileLINE1    "Initial seed = %d\n"
  387. X#define RNGfileLINE2    \
  388. X    "Number of mrandom() calls after seeding = %ld billion + %ld\n"
  389. X#define RNGfileLINE3    "RNG state table =\n"
  390. X#define RNGfileLINEn    "   %08lx %08lx %08lx %08lx\n"
  391. X#define RNGfileLINE12    "Next value in this pseudorandom sequence = %08lx\n"
  392. X
  393. X/* Write a RNG state identifier into the user-supplied string rngid,
  394. X * which must be of length at least RNGIDSTRLEN.  If the user has not
  395. X * initialized the rng with init_rng(), restart_rng(), or reconstruct_rng(),
  396. X * abort with an error message to stderr.  Otherwise return the value of rngid.
  397. X */
  398. extern char *describe_rng (rngid)
  399. char rngid[RNGIDSTRLEN];
  400. X{
  401. X  if (rngstate[0] != RNGSTATE0) {
  402. X    fprintf(stderr, "RNG has not been initialized!\n");
  403. X    fflush(stderr);
  404. X    exit(1);
  405. X  }
  406. X  sprintf(rngid, "RNG state identifier is (%ld, %ld, %ld)\n",
  407. X         RNGseed, RNGcount1, RNGcount2);
  408. X  return(rngid);
  409. X}
  410. X
  411. X/* Create a random number statefile initialized with the given seed.
  412. X * Return 1 if file is successfully created, 0 otherwise.
  413. X */
  414. extern int init_rng (seed, filename)
  415. int seed;
  416. char *filename;
  417. X{
  418. X  reconstruct_rng(seed,0,0);
  419. X  return(save_rng(filename));
  420. X} /* end init_rng */
  421. X
  422. X/* Rebuild a random() state by reseeding the generator, then making
  423. X * (count2*1e9 + count1) calls to mrandom().  Useful for error-checking
  424. X * and error-recovery routines, although it is very slow for large counts.
  425. X */
  426. extern void reconstruct_rng (seed, count1, count2)
  427. long seed, count1, count2;
  428. X{
  429. X  double a;
  430. X  initstate(seed, rngstate, RNGSTATELENGTH*4);
  431. X  RNGseed = seed; /* keep a record of the seed */
  432. X  RNGcount1 = 0;  /* mod-billion counter */
  433. X  RNGcount2 = 0;  /* div-billion counter */
  434. X  /* watch out for infinite loops */
  435. X  if (count1 >= 0 && count1 < BILLION && count2 >= 0) {
  436. X    if (count2 != 0) {
  437. X      fprintf(stderr, "Warning: this reconstruction will take a LONG time!\n");
  438. X      fflush(stderr);
  439. X    }
  440. X    for ( ; (RNGcount1 != count1) || (RNGcount2 != count2) ; ) {
  441. X      a = frandom();
  442. X    }
  443. X  }
  444. X} /* end reconstruct_rng */
  445. X
  446. X/* Return the next random() output without disturbing the RNG state.
  447. X   This procedure will only work properly if random's state is in rngstate[].
  448. X */
  449. int nextval()
  450. X{
  451. X  long state[RNGSTATELENGTH];
  452. X  long i, r, retval;
  453. X  for (i=0; i<RNGSTATELENGTH; i++) {
  454. X    state[i] = rngstate[i];
  455. X  }
  456. X  retval = random(); /* next value in this pseudorandom sequence */
  457. X  for (i=1; i<RNGSTATELENGTH-1; i++) {
  458. X    r = random(); /* these calls are necessary to restore random()'s state */
  459. X  }
  460. X  for (i=0; i<RNGSTATELENGTH; i++) {
  461. X    rngstate[i] = state[i];
  462. X  }
  463. X  return(retval);
  464. X}
  465. X
  466. X/* Restart a generator from a statefile.  Print a message on stderr
  467. X * if the restart failed due to a garbled or non-existent statefile, 
  468. X * and return 0.  Otherwise return 1.
  469. X */
  470. extern int restart_rng(filename)
  471. char *filename;
  472. X{
  473. X  FILE *fp;
  474. X  long i, m, newstate[RNGSTATELENGTH], r; /* temps */
  475. X  int errflag; /* initially 0, becomes 1 if a problem is discovered */
  476. X
  477. X  /* restore random()'s internal variables to their initial state */
  478. X  m = (RNGcount1%(RNGSTATELENGTH-1)) + RNGcount2*(BILLION%(RNGSTATELENGTH-1));
  479. X  for ( ; m%(RNGSTATELENGTH-1) != 0; m++) {
  480. X    r = random();
  481. X  }
  482. X
  483. X  /* restore counter values, retrieve original RNG seed and current state */
  484. X  fp = fopen(filename, "r");
  485. X  if (!fp) {
  486. X    fprintf(stderr, "There is no RNG statefile in this directory!\n");
  487. X    fflush(stderr);
  488. X    exit(1);
  489. X  }
  490. X  fscanf(fp, RNGfileLINE1, &RNGseed);
  491. X  fscanf(fp, RNGfileLINE2, &RNGcount2, &RNGcount1);
  492. X  fscanf(fp, RNGfileLINE3);
  493. X  for (i=0; i<RNGSTATELENGTH; i++) {
  494. X    fscanf(fp, "%lx", &newstate[i]);
  495. X  }
  496. X  fscanf(fp, "\n");
  497. X  fscanf(fp, RNGfileLINE12, &RNGnextval);
  498. X  fclose(fp);
  499. X
  500. X  /* error check: the first word of a 32-word rngstate is always 3 */
  501. X  if (newstate[0] != RNGSTATE0) {
  502. X    errflag = 1;
  503. X  } else {
  504. X    errflag = 0;
  505. X  }
  506. X
  507. X  /* tell random() we have a 32-word rngstate */
  508. X  rngstate[0] = RNGSTATE0;
  509. X  setstate(rngstate);
  510. X
  511. X  /* If reconstruction will be rapid, do it as an additional error check. */ 
  512. X  if (RNGcount1 < 50 && RNGcount2 == 0) {
  513. X    reconstruct_rng(RNGseed, RNGcount1, RNGcount2);
  514. X    /* see if we got to the same state */
  515. X    for (i=0; i<RNGSTATELENGTH; i++) {
  516. X      if (newstate[i] != rngstate[i]) {
  517. X        errflag = 1;
  518. X      }
  519. X    }
  520. X  } else {
  521. X    /* quickly modify random()'s internal state to line up with RNGcount info */
  522. X    m = (RNGcount1%(RNGSTATELENGTH-1)) + RNGcount2*(BILLION%(RNGSTATELENGTH-1));
  523. X    for (i=0; i < m%(RNGSTATELENGTH-1); i++) {
  524. X      r = random();
  525. X    }
  526. X  }
  527. X
  528. X  /* copy in the new state */
  529. X  for (i=0; i<RNGSTATELENGTH; i++) {
  530. X    rngstate[i] = newstate[i];
  531. X  }
  532. X
  533. X  /* Check nextval() operation, and verify RNGnextval */
  534. X  if (nextval() != nextval() || RNGnextval != nextval()) {
  535. X    errflag = 1;
  536. X  }
  537. X
  538. X  if (errflag) {
  539. X    fprintf(stderr,
  540. X    "Warning: RNG statefile is inconsistent.  Did you edit it?\n");
  541. X    fprintf(stderr,
  542. X    "If not, check your program to make sure you:\n");
  543. X    fprintf(stderr,
  544. X    "   1. use frandom() or mrandom(m), not random();\n");
  545. X    fprintf(stderr,
  546. X    "   2. use init_rng(seed, filename), not srandom(seed);\n");
  547. X    fprintf(stderr,
  548. X    "   3. use restart_rng(filename), not setstate(state); and\n");
  549. X    fprintf(stderr,
  550. X    "   4. don't overwrite the private storage of mrandom.\n");
  551. X    fflush(stderr);
  552. X    return(0);
  553. X  } else {
  554. X    return(1); /* everything looks ok */
  555. X  }
  556. X} /* end restart_rng */
  557. X
  558. X/* Generate a uniformly-distributed number a, 0.0 <= a < 1.0, using
  559. X * the 4.3bsd additive congruential generator random().
  560. X *
  561. X * This routine keeps track of the number of calls to random().
  562. X *
  563. X * From inspection of the object code for random() in a Sun-3 release,
  564. X * I deduce the following.  A call to initstate() or setstate()
  565. X *   1. defines the location of the state array (rngstate in the code below);
  566. X *   2. defines the length of the state array (32 words in the code below;
  567. X *    other possibilities are 2, 8, 16, or 64 words);
  568. X *   3. initializes several static variables to point at the state array
  569. X *    (the indices j and k in the code below);
  570. X *   4. defines the randomization algorithm (a linear congruential
  571. X *    generator is used if the state array has length 2).
  572. X * Additionally, initstate(seed) fills the state array with the output
  573. X * of a linear congruential generator that is seeded with the given seed.
  574. X *
  575. X * Here is a disassembled and decompiled listing of random(), as
  576. X * it would operate when initialized with my init_rng routine:
  577. X *
  578. X * long random()
  579. X * {
  580. X *   static int j=4, k=1;
  581. X *   long r;   
  582. X *   rngstate[j] += rngstate[k];
  583. X *   r = (rngstate[j]>>1) & 0x7fffffff;
  584. X *   j++;
  585. X *   if (j>31) j=1;
  586. X *   k++;
  587. X *   if (k>31) k=1;
  588. X *   return(r);
  589. X * }
  590. X *
  591. X * Note that j and k return to their original values after every 31
  592. X * calls to random().  This property allows me to write a restart_rng()
  593. X * routine even though I can't read the values of j and k. 
  594. X *
  595. X * Also note that this is a very poor RNG algorithm if it is only
  596. X * called once between successive setstate() calls.  In this pathological
  597. X * case, each output of random() will differ from the prior random() output
  598. X * by rngstate[1]/2 or rngstate[1]/2+1, mod 2^31-1.  Even in non-pathological
  599. X * calling sequences the long-period property of an additive congruential
  600. X * generator is only guaranteed if j and k always move through the array
  601. X * in the manner shown.  Thus the need for counting random() calls
  602. X * (or for writing your own time-optimized additive congruential generator).
  603. X *
  604. X * Finally, note that integer overflow is a frequent occurrence in
  605. X * rngstate[k] += rngstate[j].  Thus random() could conceivably
  606. X * behave differently on different machines.
  607. X */
  608. extern double frandom ()
  609. X{
  610. X  RNGcount1++; /* mod-billion counter */
  611. X  if (RNGcount1 == BILLION) {
  612. X    RNGcount1 = 0;
  613. X    RNGcount2++; /* an overflow is unlikely in my lifetime */
  614. X  }
  615. X  return( (double) random()/MAXLONG );
  616. X} /* end frandom */
  617. X
  618. X/* Generate a random integer, uniformly distributed in the range 0..m-1.
  619. X * We use the most-significant bits of the result of a random() call,
  620. X * although this may only marginally improve the quality of our output.
  621. X * You may wish to rewrite this routine if you don't have hardware
  622. X * floating point.  If you do, be sure to include the counter-bumping
  623. X * code from frandom(). 
  624. X */
  625. extern long mrandom (m)
  626. long m;
  627. X{
  628. X  return( (int) (frandom()*m) );
  629. X} /* end mrandom */
  630. X
  631. X/* Save the RNG state to a statefile, after calling random() enough
  632. X * times to reset its internal state variables to their initial values.
  633. X * Check to be sure the RNG can be restarted by calling restart_rng().
  634. X * Return 0 if a problem is detected, printing an error message on stderr.
  635. X * Otherwise return 1.
  636. X */
  637. extern int save_rng(filename)
  638. char *filename;
  639. X{
  640. X  FILE *fp;
  641. X  long i;
  642. X
  643. X  /* write the statefile */
  644. X  fp = fopen(filename, "w");
  645. X  if (!fp) {
  646. X    fprintf(stderr, "Trouble opening RNG statefile %s for writing.", filename);
  647. X    fflush(stderr);
  648. X    return(0);
  649. X  }
  650. X  fprintf(fp, RNGfileLINE1, RNGseed);
  651. X  fprintf(fp, RNGfileLINE2, RNGcount2, RNGcount1);
  652. X  fprintf(fp, RNGfileLINE3);
  653. X  for (i=0; i<RNGSTATELENGTH; i+=4) {
  654. X    fprintf(fp, RNGfileLINEn,
  655. X        rngstate[i], rngstate[i+1], rngstate[i+2], rngstate[i+3]);
  656. X  }
  657. X  fprintf(fp, RNGfileLINE12, nextval());
  658. X  fclose(fp);
  659. X
  660. X  /* Return after checking that state was saved correctly. */
  661. X  return( restart_rng(filename) );
  662. X
  663. X} /* end save_rng */
  664. X
  665. X/* end mrandom.c */
  666. END_OF_FILE
  667. if test 10569 -ne `wc -c <'mrandom.c'`; then
  668.     echo shar: \"'mrandom.c'\" unpacked with wrong size!
  669. fi
  670. # end of 'mrandom.c'
  671. fi
  672. if test -f 'mrandom.h' -a "${1}" != "-c" ; then 
  673.   echo shar: Will not clobber existing file \"'mrandom.h'\"
  674. else
  675. echo shar: Extracting \"'mrandom.h'\" \(2379 characters\)
  676. sed "s/^X//" >'mrandom.h' <<'END_OF_FILE'
  677. X/*
  678. X *        mrandom.h
  679. X *
  680. X *    A wrapper for random(), to allow safe restart.
  681. X *
  682. X *    Original Implementation:
  683. X *        Clark Thomborson, September 1991
  684. X *
  685. X *    This material is based upon work supported by the National
  686. X *    Science Foundation under grant number MIP-9023238.  The
  687. X *    Government has certain rights in this material.
  688. X *
  689. X *    Any opinions, findings, and conclusions or recommendations
  690. X *    expressed in this material are those of the author and do
  691. X *    not necessarily reflect the view of the National Science
  692. X *    Foundation.
  693. X *
  694. X *    This code is neither copyrighted nor patented.
  695. X */
  696. X
  697. X#define RNGIDSTRLEN 80 /* max length of string written by describe_rng() */
  698. X
  699. char *describe_rng (/* char rngid[RNGIDSTRLEN] */);
  700. X/* Write a RNG state identifier into the user-supplied string rngid,
  701. X * which must be of length at least RNGIDSTRLEN.  If the user has not
  702. X * initialized the rng with init_rng(), restart_rng(), or reconstruct_rng(),
  703. X * abort with an error message to stderr.  Otherwise return the value of rngid.
  704. X */
  705. X
  706. int init_rng (/* long seed; char *filename; */);
  707. X/* Create a random number statefile initialized with the given seed.
  708. X * Return 1 if file is successfully created, 0 otherwise.
  709. X */
  710. X
  711. int restart_rng (/* char *filename; */);
  712. X/* Restart a generator from a statefile.  Print a message on stderr
  713. X * if the restart failed due to a garbled or non-existent statefile, 
  714. X * and return 1.  Otherwise return 0.
  715. X */
  716. X
  717. double frandom ();
  718. X/* Generate a uniformly-distributed number a, 0.0 <= a < 1.0, using
  719. X * the 4.3bsd additive congruential generator random().
  720. X */ 
  721. X
  722. long mrandom (/* long m; */);
  723. X/* Generate a random integer, uniformly distributed in the range 0..m-1.
  724. X * We use the most-significant bits of the result of a random() call.
  725. X */
  726. X
  727. int save_rng(/* char *filename; */);
  728. X/* Save the RNG state to a statefile, after calling random() enough
  729. X * times to reset its internal state variables to their initial values.
  730. X * Check to be sure the RNG can be restarted by calling restart_rng().
  731. X * Return 0 if a problem is detected, printing an error message on stderr.
  732. X * Otherwise return 1.
  733. X */
  734. X
  735. void reconstruct_rng (/* long seed, count1, count2; */);
  736. X/* Rebuild a random() state by reseeding the generator, then making
  737. X * (count2*1e9 + count1) calls to random().  Useful for error-checking
  738. X * and error-recovery routines, although it is very slow for large counts.
  739. X */
  740. X
  741. X/* end mrandom.h */
  742. END_OF_FILE
  743. if test 2379 -ne `wc -c <'mrandom.h'`; then
  744.     echo shar: \"'mrandom.h'\" unpacked with wrong size!
  745. fi
  746. # end of 'mrandom.h'
  747. fi
  748. if test -f 'mrtest.c' -a "${1}" != "-c" ; then 
  749.   echo shar: Will not clobber existing file \"'mrtest.c'\"
  750. else
  751. echo shar: Extracting \"'mrtest.c'\" \(2684 characters\)
  752. sed "s/^X//" >'mrtest.c' <<'END_OF_FILE'
  753. X/*
  754. X *        mrtest.c
  755. X *
  756. X *    Test routine for mrandom.c 
  757. X *
  758. X *    Original Implementation:
  759. X *        Clark Thomborson, September 1991
  760. X *
  761. X *    This material is based upon work supported by the National
  762. X *    Science Foundation under grant number MIP-9023238.  The
  763. X *    Government has certain rights in this material.
  764. X *
  765. X *    Any opinions, findings, and conclusions or recommendations
  766. X *    expressed in this material are those of the author and do
  767. X *    not necessarily reflect the view of the National Science
  768. X *    Foundation.
  769. X *
  770. X *    This code is neither copyrighted nor patented.
  771. X */
  772. X
  773. X# include <sys/file.h> /* we use access() */
  774. X# include "mrandom.h"
  775. X
  776. X# define RNGFILENAME "RNGstatefile" /* where the RNG state is stored */
  777. X
  778. X/* calling conventions */
  779. void argerr(progname)
  780. char *progname;
  781. X{
  782. X  printf("Usage: %s nn -snnnnnnnn -mnn R\n", progname);
  783. X  printf("  nn sets number of random generates\n");
  784. X  printf("  -snnnnnnnn seeds the RNG with the given value\n");
  785. X  printf("  -mnnnn adjusts the range of the RNG\n");
  786. X  exit(1);
  787. X} /* end argerr */
  788. X
  789. X/* A simple test-driver */
  790. int main(argc,argv)
  791. int argc; char *argv[];
  792. X{
  793. X  /* command-line arguments, with default values */
  794. X  int reseeding=0; /* nonzero if RNG state will be re-initialized */
  795. X  int seed=1573122697; /* new seed for RNG */
  796. X  int n=10; /* number of randoms to generate */
  797. X  int m=100; /* desired range of random outputs: 0..m-1 */
  798. X
  799. X  int i; /* temp */
  800. X  char rngdesc[RNGIDSTRLEN]; /* for a describe_rng() call */
  801. X
  802. X  if(argc > 1) {
  803. X    for(i=1;i<argc;i++) {
  804. X      if(argv[i][0] >= '0' && argv[i][0] <= '9') {
  805. X    n = atoi(&(argv[i][0]));
  806. X      } else if(argv[i][0] == '-') {
  807. X    switch(argv[i][1]) {
  808. X    case 's': /* new seed for rng */
  809. X      seed = atoi(&(argv[i][2]));
  810. X      reseeding = 1;
  811. X      break;
  812. X    case 'm': /* adjust range of rng */
  813. X      m = atoi(&(argv[i][2]));
  814. X      break;
  815. X    default:
  816. X      argerr((char*) argv[0]);
  817. X    }
  818. X      } else {
  819. X    argerr((char*) argv[0]);
  820. X      }
  821. X    }
  822. X  }
  823. X  
  824. X  if (!reseeding ) {
  825. X    if (access(RNGFILENAME, R_OK)) {
  826. X      printf("There is no RNG statefile in this directory, so ");
  827. X      printf("I'll make one for you.\n");
  828. X      reseeding = 1;
  829. X    }
  830. X  }
  831. X
  832. X  if (reseeding) { /* create a new statefile from scratch */
  833. X    if (!init_rng(seed,RNGFILENAME)) {
  834. X      exit(1);
  835. X    }
  836. X    printf("Initializing RNG.   ");
  837. X  } else { /* use an existing statefile */
  838. X    if (!restart_rng(RNGFILENAME)) {
  839. X      exit(1);
  840. X    }
  841. X    printf("Restarting RNG.  ");
  842. X  }
  843. X  printf(describe_rng(rngdesc));
  844. X
  845. X  printf("Here are %d random values in the range 0 to %d:\n", n, m-1);
  846. X  for (i=0; i<n; i++) {
  847. X    printf("  %d\n", mrandom(m));
  848. X  }
  849. X
  850. X  /* terminate job */
  851. X  printf("Final ");
  852. X  printf(describe_rng(rngdesc));
  853. X
  854. X  return( save_rng(RNGFILENAME) );
  855. X
  856. X} /* end main */
  857. END_OF_FILE
  858. if test 2684 -ne `wc -c <'mrtest.c'`; then
  859.     echo shar: \"'mrtest.c'\" unpacked with wrong size!
  860. fi
  861. # end of 'mrtest.c'
  862. fi
  863. echo shar: End of archive 1 \(of 1\).
  864. cp /dev/null ark1isdone
  865. MISSING=""
  866. for I in 1 ; do
  867.     if test ! -f ark${I}isdone ; then
  868.     MISSING="${MISSING} ${I}"
  869.     fi
  870. done
  871. if test "${MISSING}" = "" ; then
  872.     echo You have the archive.
  873.     rm -f ark[1-9]isdone
  874. else
  875.     echo You still need to unpack the following archives:
  876.     echo "        " ${MISSING}
  877. fi
  878. ##  End of shell archive.
  879. exit 0
  880.